﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

public class ABController : EditorWindow
{
    #region
    [MenuItem("Assets/资源/设置打包", false, 2)]
    public static void OpenAssetBundle()
    {
        isAllBundle = true;
        isChangeOnly = false;
        ReadEditorPrefs();
        GetWindow<ABController>().Show();
    }

    [MenuItem("Assets/资源/安卓打包", false, 2)]
    public static void AndroidBundle()
    {
        isAllBundle = true;
        isChangeOnly = false;
        buildTarget = BuildTarget.Android;
        ReadEditorPrefs();
        CreateAssetBundle();
    }

    [MenuItem("Assets/资源/IOS打包", false, 2)]
    public static void IOSBundle()
    {
        isAllBundle = true;
        isChangeOnly = false;
        buildTarget = BuildTarget.iOS;
        ReadEditorPrefs();
        CreateAssetBundle();
    }

    [MenuItem("Assets/资源/当前所选资源安卓打包", false, 2)]
    public static void CurSelectAndroidBundle()
    {
        isAllBundle = false;
        isChangeOnly = false;
        buildTarget = BuildTarget.Android;
        ReadEditorPrefs();
        CreateAssetBundle();
    }

    [MenuItem("Assets/资源/当前所选资源IOS打包", false, 2)]
    public static void CurSelectIOSBundle()
    {
        isAllBundle = false;
        isChangeOnly = false;
        buildTarget = BuildTarget.iOS;
        ReadEditorPrefs();
        CreateAssetBundle();
    }

    [MenuItem("Assets/资源/对更新的资源进行安卓打包", false, 2)]
    public static void ExchangeAndroidBundle()
    {
        isAllBundle = true;
        isChangeOnly = true;
        buildTarget = BuildTarget.Android;
        ReadEditorPrefs();
        CreateAssetBundle();
    }

    [MenuItem("Assets/资源/对更新的资源进行IOS打包", false, 2)]
    public static void ExchangeIOSBundle()
    {
        isAllBundle = true;
        isChangeOnly = true;
        buildTarget = BuildTarget.iOS;
        ReadEditorPrefs();
        CreateAssetBundle();
    }
    #endregion

    //是否全量打包
    private static bool isAllBundle = false;
    //是否只打包改变的资源
    private static bool isChangeOnly = false;
    private static int typeSwitch = int.MaxValue;

    private const string cfgPath = "/Editor/AB/";
    private const string luaPath = "/Lua";
    private const string assetsPath = "Assets" + cfgPath;
    private static string dependsPath = "ABDepends.json";
    private static string vsConfigPath = "VsConfig.json";
    private static string verPath = "Ver.json";
    private static string crcPath = "Crc.json";

    private static List<CreateABBase> createList = new List<CreateABBase>();

    public static void CreateAssetBundle()
    {
        EditorApplication.isPlaying = false;
        double lastTime = EditorApplication.timeSinceStartup;
        if (isAllBundle && typeSwitch == int.MaxValue && !isChangeOnly)
        {
            if (EditorUtility.DisplayDialog("文件清理", "需要删除之前打包的旧文件吗？", "删除", "不删除"))
            {
                DeleteTempFiles(Application.streamingAssetsPath);
            }
        }

        //设置编译参数 
        BuildOptions = BuildAssetBundleOptions.CollectDependencies;
        if (EditorUserBuildSettings.activeBuildTarget != buildTarget)
        {
            string str = "检测到现在打包的平台和设置的平台不一致，继续打包将自动转换平台，是否继续打包？";
            if (!EditorUtility.DisplayDialog("平台转换", str, "继续", "退出"))
            {
                return;
            }
            //切换到对应平台
            EditorUserBuildSettings.SwitchActiveBuildTarget(buildTarget);
        }

        Caching.CleanCache();
        Clear();

        //获得crc列表
        GetCrcList();

        //初始化类型
        InitCreate();    // 这个是创建记录所有需要进行打包的AssetBundle的列表，并且对列表的类型进行赋值。这一步其实什么都没有做。
        //资源分类
        AddAssetToCreate();     //这个是对resources文件夹下的资源进行分类，并且将资源放到各个要打包的列表中。（这一步中，将resources里用到的和关联的东西都做了分类，也就是说里面用到的脚本、shader等都已经分好类了，对更新资源打包的判断主要在这一步）

        //资源排序
        AssetSort();            //将已经分好类的resources文件夹下的资源按照引用次数进行排序(可以在这里判断引用次数小于等于0的打到一个包中，避免加载时过多消耗io性能)

        //打包
        Create();               //将排好序的资源进行打包，这个步骤中把哪些脚本什么的需要打在一起的资源也给打包了

        //生成crc文件字典
        CreateCrcDictionary();            //这一步将每个已经打包的资源创建一个crc值

        //写入依赖文件配置  
        WriteConfig();          //这一步已经将resources下的资源文件全部打包完成，将依赖关系写入文件，并将依赖配置文件打包。这个依赖关系是总的资源依赖配置表。游戏中场景加载和场景预加载都要用到这个。

        //打包Dll
        CreateDllBundle();

        //打包Lua
        CreateLuaBundle();       //打包lua(指定的文件夹的lua打包到一个ab包，获取的时候也跟其他的资源不一样，根据自己的规则读取)

        AssetDatabase.Refresh();
        //创建md5 配置
        CreateMD5();               //md5是为已经打完包的ab包进行生成md5值
        //清空
        Clear();

        lastTime = EditorApplication.timeSinceStartup - lastTime;
        EDebug.Log(string.Format("打包完成，共耗时{0}秒", lastTime));
    }


    /// <summary>
    /// 删除这个目录下的所有文件及文件夹
    /// </summary>
    /// <param name="strPath"></param>
    private static void DeleteTempFiles(string strPath)
    {
        //删除这个目录下的所有子目录
        string[] dirArr = Directory.GetDirectories(strPath);
        if (dirArr.Length > 0)
        {
            for (int i = 0; i < dirArr.Length; i++)
            {
                EditorUtility.DisplayProgressBar("正在删除旧文件夹", dirArr[i], (i + 1f) / dirArr.Length);
                Directory.Delete(dirArr[i], true);
            }
        }
        string[] fileArr = Directory.GetFiles(strPath);
        //删除这个目录下的所有文件
        if (fileArr.Length > 0)
        {
            for (int i = 0; i < fileArr.Length; i++)
            {
                if (fileArr[i].EndsWith(".mp4"))
                {
                    continue;
                }
                EditorUtility.DisplayProgressBar("正在删除旧文件", fileArr[i], (i + 1f) / fileArr.Length);
                File.Delete(fileArr[i]);
            }
        }
    }

    /// <summary>
    /// 生成md5 
    /// </summary>
    private static void CreateMD5()
    {
        MD5CryptoServiceProvider md5Generator = new MD5CryptoServiceProvider();
        ABInfo info = new ABInfo(assetsPath + vsConfigPath, 0);                        //md5配置文件（优先级是最高的）
        ABInfo.allInfoDic.Remove(info.path);                                                  //配置信息中剔除md5配置
        if (File.Exists(info.SavePath))                                                       //如果文件已经存在
        {
            File.Delete(info.SavePath);                                                       //将文件删除
        }
        string[] paths = Directory.GetFiles(Application.streamingAssetsPath, "*.uab", SearchOption.AllDirectories);             //获取所有的AB包路径
        FileData fileList = new FileData();

        for (int i = 0; i < paths.Length; i++)
        {
            FileStream file = new FileStream(paths[i], FileMode.Open, FileAccess.Read, FileShare.Read);                          //读取ab文件
            byte[] hash = md5Generator.ComputeHash(file);                                                                        //计算哈希值
            string strMD5 = BitConverter.ToString(hash).Replace("-", "");

            ResData resData = new ResData();
            resData.mDataPath = Path.GetFileName(paths[i]);                                                                      //写入ab包路径
            resData.mDataMD5 = strMD5;                                                                                           //写入md5值
            resData.mSize = (uint)file.Length;                                                                                   //md5长度
            fileList.AddResData(resData);                                                                                        //记录ab包的数据
            file.Close();
        }

        WriteFile(vsConfigPath, fileList.ToString());
        AssetDatabase.Refresh();

        Object configObj = AssetDatabase.LoadAssetAtPath(assetsPath + vsConfigPath, typeof(TextAsset));
        CreateAB<TextAsset> createAB = new CreateAB<TextAsset>(true, 0);
        createAB.AddAsset(configObj, assetsPath + vsConfigPath, typeof(TextAsset));
        createAB.Create();                                                                                                        //将md5配置文件进行打包
        AssetDatabase.Refresh();

        AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(info.SavePath.Replace("\\", "/")));
        string fileName = info.SavePath;
        FileInfo fileInfo = new FileInfo(fileName);
        fileInfo.Directory.Create();
        File.WriteAllBytes(fileName, (ab.mainAsset as TextAsset).bytes);
        AssetDatabase.Refresh();

        Object verObj = AssetDatabase.LoadAssetAtPath(assetsPath + verPath, typeof(TextAsset));
        string verSavePath = Application.streamingAssetsPath + "/" + verPath + ".uab";                                                       //版本
        File.WriteAllBytes(verSavePath, (verObj as TextAsset).bytes);
        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 写入资源依赖配置文件
    /// </summary>
    private static void WriteConfig()
    {
        string path = "Assets/AB_"; ABDepends abDepends = null;
        string fullPath = Application.dataPath + cfgPath + dependsPath;
        if (!script && !config && ((isAllBundle && !isChangeOnly) || !File.Exists(fullPath)))
        {                // 读取依赖配置文件
            abDepends = new ABDepends();
        }
        else
        {
            TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(assetsPath + dependsPath);
            abDepends = JsonConvert.DeserializeObject<ABDepends>(textAsset.text);
        }
        foreach (var item in ABInfo.allInfoDic)                                                   //这个allInfoDic是在资源分类的时候记录的，将资源和资源的路径以键值对的形式存起来。（不包括统一打包的资源如脚本、shader等）
        {
            List<string> ABDepends = null;
            //对于只打改变资源的选项，有些特殊,需要对依赖配置关系重新整理一下(手动添加依赖关系，因为它的依赖关系被剥离了)
            if (isChangeOnly)
            {
                ABDepends = item.Value.Depends.ToList();
                for (int i = ABDepends.Count - 1; i >= 0; i--)
                {
                    string str = Path.GetExtension(ABDepends[i]);
                    bool replace = str != ".cs" && str != ".shader" && str != ".json";

                    if (replace && !ABDepends[i].Equals(item.Value.path) && IsResourceFile(ABDepends[i]))
                    {
                        ABDepends[i] = PathUtil.ReplaceString(ABDepends[i]) + ".uab";
                    }
                    else { ABDepends.Remove(ABDepends[i]); }
                }
            }
            else
            {
                //获得资源的依赖关系
                ABDepends = item.Value.ABDepends;                                        
            }
            if (ABDepends.Count > 0)
            {
                abDepends.AddDepend(item.Value.ABPath, ABDepends.ToArray());
            }
        }

        WriteFile(dependsPath, JsonConvert.SerializeObject(abDepends, Formatting.Indented));

        AssetDatabase.CreateAsset(abDepends, path + dependsPath);
        string file = Application.streamingAssetsPath + "/AB_" + dependsPath + ".uab";
        BuildPipeline.BuildAssetBundle(abDepends, null, file, BuildOptions, BuildTarget);
        AssetDatabase.DeleteAsset(path + dependsPath);
        EditorUtility.UnloadUnusedAssetsImmediate();
    }

    private static void CreateDllBundle()
    {
        string path = "Assets/Resources/Dlls/";
        string[] dllPath = Directory.GetFiles(path, "*.bytes", SearchOption.AllDirectories);
        if (dllPath.Length > 0)
        {
            Object[] objs = new Object[dllPath.Length];
            for (int i = 0; i < dllPath.Length; i++)
            {
                objs[i] = AssetDatabase.LoadAssetAtPath(dllPath[i], typeof(TextAsset));
            }
            string file = Application.streamingAssetsPath + "/Hotcode.dll.bytes" + ".uab";
            BuildPipeline.BuildAssetBundle(null, objs, file, BuildOptions, BuildTarget);
        }
    }

    private static void CreateLuaBundle()
    {
        string assetPath = "Assets/lua.asset";  //创建lua的打包路径
        string luaScriptPath = Application.dataPath + luaPath;
        string[] scriptPath = Directory.GetFiles(luaScriptPath, "*.lua", SearchOption.AllDirectories);                   //获得指定文件夹下的所有lua文件   

        if (scriptPath.Length > 0)
        {
            LuaData data = CreateInstance<LuaData>();                                                            //lua管理数据
            data.names = new List<string>();
            data.codes = new List<string>();
            for (int i = 0; i < scriptPath.Length; i++)                                                                          //将lua文件数据添加到管理数据中
            {
                string code = File.ReadAllText(scriptPath[i]);
                data.codes.Add(code);
                string strPath = scriptPath[i].Replace(".lua", "");

                string p = strPath.Replace("\\", "/").Replace(luaScriptPath, "").Replace("/", "_");
                data.names.Add(p);
                EDebug.Log("Create Lua Asset Bundle " + p);
            }

            AssetDatabase.CreateAsset(data, assetPath);                                 //在指定的路径新建资源。                                     
            Object obj = AssetDatabase.LoadAssetAtPath(assetPath, typeof(LuaData));     //读取luadata资源
            string file = Application.streamingAssetsPath + "/Lua.uab";
            BuildPipeline.BuildAssetBundle(obj, null, file, BuildOptions, BuildTarget);
            AssetDatabase.DeleteAsset(assetPath);
        }
    }

    private static void AddAssetToCreate()
    {
        string[] allPath = null;
        if (isAllBundle)
        {
            string fullPath = Application.dataPath + "/Resources";
            allPath = Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories);
            for (int i = 0; i < allPath.Length; i++)
            {
                EditorUtility.DisplayProgressBar("正在获取资源信息", allPath[i], (i + 1f) / allPath.Length);
                allPath[i] = allPath[i].Replace("\\", "/").Replace(Application.dataPath, "Assets");
            }
        }
        else
        {
            for (int i = 0; i < createList.Count; i++)
            {
                createList[i].Init();
            }
            Object[] objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
            allPath = new string[objs.Length];
            for (int i = 0; i < objs.Length; i++)
            {
                allPath[i] = AssetDatabase.GetAssetPath(objs[i]);
            }
        }

        allPath = AssetDatabase.GetDependencies(allPath);
        //最终需要打包的项
        List<string> finalChangeList = new List<string>();
        //预设改变的物体列表
        List<string> prefabChangeList = new List<string>();

        Object obj = null;
        StringBuilder typeErrorSB = new StringBuilder();
        StringBuilder loadErrorSB = new StringBuilder();

        for (int i = 0; i < allPath.Length; i++)
        {
            EditorUtility.DisplayProgressBar("正在对资源进行分类", allPath[i], (i + 1f) / allPath.Length);
            obj = AssetDatabase.LoadAssetAtPath(allPath[i], typeof(Object));
            if (obj != null)
            {
                if (isChangeOnly)
                {
                    //过滤掉脚本文件            
                    for (int j = 0; j < createList.Count; j++)
                    {
                        if (createList[j].AddAsset(obj, allPath[i], obj.GetType()))
                        {
                            if (IsFileChanged(allPath[i]))
                            {
                                if (Path.GetExtension(allPath[i]) == ".prefab")
                                {
                                    prefabChangeList.Add(allPath[i]);
                                }
                                else
                                {
                                    finalChangeList.Add(allPath[i]);
                                }
                            }
                            else if (Path.GetExtension(allPath[i]) != ".prefab")
                            {
                                //非prefab的未改变资源直接从打包列表中剔除,没有改变的prefab直接添加，在后面对比的时候再删除
                                createList[j].RemoveAsset(obj, allPath[i], obj.GetType());
                            }
                            break;
                        }
                        else if (j == createList.Count - 1 && typeSwitch == int.MaxValue)
                        {
                            typeErrorSB.AppendLine(allPath[i]);
                        }
                    }
                }
                else
                {
                    //过滤掉脚本文件       
                    for (int j = 0; j < createList.Count; j++)
                    {
                        if (createList[j].AddAsset(obj, allPath[i], obj.GetType()))
                        {
                            break;
                        }
                        else if (j == createList.Count - 1 && typeSwitch == int.MaxValue)
                        {
                            typeErrorSB.AppendLine(allPath[i]);
                        }
                    }
                }
                obj = null;
                EditorUtility.UnloadUnusedAssetsImmediate();
            }
            else
            {
                loadErrorSB.AppendLine(allPath[i]);
            }
        }
        if (typeErrorSB.Length > 0)
        {
            EDebug.Log(string.Format("以下资源找不到合适类型，请查证\n{0}", typeErrorSB.ToString()));
        }
        if (loadErrorSB.Length > 0)
        {
            EDebug.Log(string.Format("以下资源加载失败，请查证\n{0}", loadErrorSB.ToString()));
        }

        //如果只打包改变的资源，需要在这里进行排序将没有改变的资源剔除（主要分为预设和非预设，非预设只要改变了，就要打包，预设改变需要找出他的父级引用物体一起打包）
        if (isChangeOnly)
        {
            //获取字典中的所有key，用于检测文件是否改变
            List<string> keyList = ABInfo.allInfoDic.Keys.ToList();
            //对allInfoDic中的prefab检测，是否有引用到改变的资源的预设，将这些预设也记录出来
            List<string> dependPrefabList = new List<string>();

            for (int i = 0; i < keyList.Count; i++)
            {
                if (Path.GetExtension(keyList[i]) != ".prefab")
                {
                    continue;
                }
                ABInfo item = ABInfo.allInfoDic[keyList[i]];
                EditorUtility.DisplayProgressBar("正在查找改变资源的父级引用", keyList[i], (i + 1f) / keyList.Count);
                List<string> depends = item.Depends.ToList();
                for (int j = 0; j < prefabChangeList.Count; j++)
                {
                    if (keyList[i] != prefabChangeList[j] && depends.Contains(prefabChangeList[j]))
                    {
                        dependPrefabList.Add(keyList[i]);
                        break;
                    }
                }
            }
            //将资源改变的prefab添加到打包项
            for (int i = 0; i < prefabChangeList.Count; i++)
            {
                finalChangeList.Add(prefabChangeList[i]);
            }
            //将资源改变项的引用资源添加到打包项
            for (int i = 0; i < dependPrefabList.Count; i++)
            {
                finalChangeList.Add(dependPrefabList[i]);
            }

            //剔除createList中没有改变的资源(这一步主要是为了剔除没有引用改变prefab资源的prefab)
            for (int i = 0; i < keyList.Count; i++)
            {
                if (Path.GetExtension(keyList[i]) != ".prefab") { continue; }

                ABInfo item = ABInfo.allInfoDic[keyList[i]];
                EditorUtility.DisplayProgressBar("正在剔除不需要打包的文件", keyList[i], (i + 1f) / keyList.Count);
                if (!finalChangeList.Contains(keyList[i]))
                {
                    obj = AssetDatabase.LoadAssetAtPath(keyList[i], typeof(Object));
                    if (obj != null)
                    {
                        for (int j = 0; j < createList.Count; j++)
                        {
                            createList[j].RemoveAsset(obj, item.path, obj.GetType());
                            break;
                        }
                    }
                    EditorUtility.UnloadUnusedAssetsImmediate();
                }
            }
        }
        EditorUtility.ClearProgressBar();
    }

    private static void AssetSort()
    {
        for (int i = 0; i < createList.Count; i++)
        {
            createList[i].Sort();
        }
    }

    private static void Create()
    {
        for (int i = 0; i < createList.Count; i++)
        {
            BuildPipeline.PushAssetDependencies();
            createList[i].Create();
        }
        for (int i = 0; i < createList.Count; i++)
        {
            BuildPipeline.PopAssetDependencies();
        }
    }

    private static void Clear()
    {
        crcDictionary.Clear();
        crcList.Clear();
        ABInfo.allInfoDic.Clear();
        createList.Clear();

        AssetDatabase.Refresh();
        EditorUtility.UnloadUnusedAssetsImmediate();
    }

    private static void InitCreate()
    {
        ABInfo.allInfoDic.Clear();
        createList.Clear();

        if ((typeSwitch >> (int)CreateType.script & 1) == 1)
            createList.Add(new CreateInOneAB<MonoScript>(true, createList.Count, "Script", cfgPath));       //脚本
        if ((typeSwitch >> (int)CreateType.config & 1) == 1)
            createList.Add(new CreateInOneAB<TextAsset>(true, createList.Count, "Config", cfgPath));        //所有json表
        if ((typeSwitch >> (int)CreateType.shader & 1) == 1)
            createList.Add(new CreateInOneAB<Shader>(true, createList.Count, "Shader", cfgPath));           //着色器
        if ((typeSwitch >> (int)CreateType.texture2D & 1) == 1)
            createList.Add(new CreateAB<Texture2D>(true, createList.Count));                                //图片
        if ((typeSwitch >> (int)CreateType.renderTexture & 1) == 1)
            createList.Add(new CreateAB<RenderTexture>(true, createList.Count));                            //渲染纹理
        if ((typeSwitch >> (int)CreateType.textAsset & 1) == 1)
            createList.Add(new CreateAB<TextAsset>(true, createList.Count));                                //文本文件
        if ((typeSwitch >> (int)CreateType.audioClip & 1) == 1)
            createList.Add(new CreateAB<AudioClip>(true, createList.Count));                                //声音文件
        if ((typeSwitch >> (int)CreateType.physicMaterial & 1) == 1)
            createList.Add(new CreateAB<PhysicMaterial>(true, createList.Count));                           //物理材质
        if ((typeSwitch >> (int)CreateType.cubemap & 1) == 1)
            createList.Add(new CreateAB<Cubemap>(true, createList.Count));                                  //天空盒
        if ((typeSwitch >> (int)CreateType.font & 1) == 1)
            createList.Add(new CreateAB<Font>(true, createList.Count));                                     //ttf字体
        if ((typeSwitch >> (int)CreateType.material & 1) == 1)
            createList.Add(new CreateAB<Material>(true, createList.Count));                                 //材质
        if ((typeSwitch >> (int)CreateType.uiAtlas & 1) == 1)
            createList.Add(new CreateAB<UIAtlas>(false, createList.Count));                                 //ngui图集
        if ((typeSwitch >> (int)CreateType.uiFont & 1) == 1)
            createList.Add(new CreateAB<UIFont>(false, createList.Count));                                  //ngui字体
        if ((typeSwitch >> (int)CreateType.gameObject & 1) == 1)
            createList.Add(new CreateAB<GameObject>(true, createList.Count));                               //预设体
    }

    private static void ReadEditorPrefs()
    {
        if (EditorPrefs.HasKey(Application.dataPath + "ab_options"))
        {
            string jsonStr = EditorPrefs.GetString(Application.dataPath + "ab_options");
            JObject jobj = JsonConvert.DeserializeObject(jsonStr) as JObject;
            buildAssetBundleOptions = jobj["buildAssetBundleOptions"].JsonToBool();
            collectDependencies = jobj["collectDependencies"].JsonToBool();
            completeAssets = jobj["completeAssets"].JsonToBool();
            disableWriteTypeTree = jobj["disableWriteTypeTree"].JsonToBool();
            deterministicAssetBundle = jobj["deterministicAssetBundle"].JsonToBool();
            uncompressedAssetBundle = jobj["uncompressedAssetBundle"].JsonToBool();
            buildTarget = jobj["buildTarget"].ToObject<BuildTarget>();
        }
    }

    private static void WriteEditorPrefs()
    {
        JObject jobj = new JObject();
        jobj["buildAssetBundleOptions"] = buildAssetBundleOptions;
        jobj["collectDependencies"] = collectDependencies;
        jobj["completeAssets"] = completeAssets;
        jobj["disableWriteTypeTree"] = disableWriteTypeTree;
        jobj["deterministicAssetBundle"] = deterministicAssetBundle;
        jobj["uncompressedAssetBundle"] = uncompressedAssetBundle;
        jobj["buildTarget"] = (int)buildTarget;

        string jsonStr = jobj.ToString();
        EditorPrefs.SetString(Application.dataPath + "ab_options", jsonStr);
    }

    private static BuildAssetBundleOptions buildOptions;
    public static BuildAssetBundleOptions BuildOptions
    {
        private set
        {
            var collectDependencies = ABController.collectDependencies ? BuildAssetBundleOptions.CollectDependencies : 0;
            var completeAssets = ABController.completeAssets ? BuildAssetBundleOptions.CompleteAssets : 0;
            var disableWriteTypeTree = ABController.disableWriteTypeTree ? BuildAssetBundleOptions.DisableWriteTypeTree : 0;
            var deterministicAssetBundle = ABController.deterministicAssetBundle ? BuildAssetBundleOptions.DeterministicAssetBundle : 0;
            var uncompressedAssetBundle = ABController.uncompressedAssetBundle ? BuildAssetBundleOptions.UncompressedAssetBundle : 0;
            buildOptions = collectDependencies | completeAssets | disableWriteTypeTree | deterministicAssetBundle | uncompressedAssetBundle;
        }
        get
        {
            return buildOptions;
        }
    }

    private static BuildTarget buildTarget = BuildTarget.Android;
    public static BuildTarget BuildTarget
    {
        get { return buildTarget; }
    }

    private static bool args = false;
    private static bool buildAssetBundleOptions = true;
    private static bool collectDependencies = true;
    private static bool completeAssets = true;
    private static bool disableWriteTypeTree = false;
    private static bool deterministicAssetBundle = true;
    private static bool uncompressedAssetBundle = false;
    private static bool config = false;
    private static bool script = false;

    void OnGUI()
    {
        args = EditorGUILayout.BeginToggleGroup("资源包编译选项(非懂勿改)", args);
        collectDependencies = EditorGUILayout.Toggle("收集依赖关系", collectDependencies);
        completeAssets = EditorGUILayout.Toggle("完整资源", completeAssets);
        disableWriteTypeTree = EditorGUILayout.Toggle("禁用写入类型树", disableWriteTypeTree);
        deterministicAssetBundle = EditorGUILayout.Toggle("确定资源包", deterministicAssetBundle);
        uncompressedAssetBundle = EditorGUILayout.Toggle("不启用压缩", uncompressedAssetBundle);
        EditorGUILayout.EndToggleGroup();

        EditorGUILayout.LabelField("打包选择的类型:");
        config = EditorGUILayout.Toggle("json表", config);
        script = EditorGUILayout.Toggle("引用脚本", script);

        buildTarget = (BuildTarget)EditorGUILayout.EnumPopup("平台", buildTarget, GUILayout.Width(300));
        GUILayout.BeginHorizontal();
        {
            bool guiEnabledTemp = GUI.enabled;
            if (GUILayout.Button("确定打包", GUILayout.Width(80)))
            {
                if (config || script)
                {
                    typeSwitch = 0;
                    if (config)
                    {
                        typeSwitch = typeSwitch | (1 << (int)CreateType.config);
                    }
                    if (script)
                    {
                        typeSwitch = typeSwitch | (1 << (int)CreateType.script);
                    }
                }
                else
                {
                    typeSwitch = int.MaxValue;
                }

                WriteEditorPrefs();
                CreateAssetBundle();
                Close();
            }
            GUI.enabled = guiEnabledTemp;
        }
        GUILayout.EndHorizontal();
    }


    #region  只对更新的资源进行打包&&CRC处理

    /// <summary>
    /// 本地crclist
    /// </summary>
    private static List<CRCJSON> crcList = new List<CRCJSON>();
    /// <summary>
    /// 记录所有要打包的文件的crc值的字典(文件的绝对路径和文件的CRC值)
    /// </summary>
    private static Dictionary<string, ulong> crcDictionary = new Dictionary<string, ulong>();

    /// <summary>
    /// 获取本地crclist
    /// </summary>
    private static void GetCrcList()
    {
        if (File.Exists(Application.dataPath + cfgPath + crcPath))
        {
            TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(assetsPath + crcPath);
            crcList = JsonConvert.DeserializeObject<List<CRCJSON>>(textAsset.text);
        }
    }

    /// <summary>
    /// 创建所有要打包的文件的crc值
    /// </summary>
    private static void CreateCrcDictionary()
    {
        //生成所有的打包文件的CRC值
        foreach (var item in ABInfo.allInfoDic)
        {
            string filePath = Application.dataPath + item.Key.Remove(0, 6);
            ulong fileCrc = GetCrcValue(filePath);
            if (crcDictionary.ContainsKey(filePath))
            {
                crcDictionary[filePath] = fileCrc;
            }
            else
            {
                crcDictionary.Add(filePath, fileCrc);
            }
        }
        WriteCrcFile();
    }

    /// <summary>
    /// 将crc字典写入crc配置文件中
    /// </summary>
    private static void WriteCrcFile()
    {
        //如果配置列表不存在，创建配置列表
        string fullPath = Application.dataPath + cfgPath + cfgPath;
        if (!File.Exists(fullPath))
        {
            FileStream fileStream = new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write, FileShare.Write);
            fileStream.Close(); crcList = new List<CRCJSON>();

            foreach (var item in crcDictionary)
            {
                CRCJSON crcjson = new CRCJSON();
                crcjson.FilePath = item.Key;
                crcjson.FileCRC = item.Value;
                crcList.Add(crcjson);
            }
        }
        //如果已经存在配置文件，需要对比文件的crc值，写入最新的crc值,这一步还需要剔除已经被删除的crc值
        else
        {
            bool isNew = false;
            foreach (var item in crcDictionary)
            {
                isNew = true;
                for (int i = 0; i < crcList.Count; i++)
                {
                    if (crcList[i].FilePath.Equals(item.Key))
                    {
                        crcList[i].FileCRC = item.Value;
                        isNew = false; break;
                    }
                }

                if (isNew)
                {
                    CRCJSON crcjson = new CRCJSON();
                    crcjson.FilePath = item.Key;
                    crcjson.FileCRC = item.Value;
                    crcList.Add(crcjson);
                }
            }

            //删除多余的crc
            List<CRCJSON> newCrcList = new List<CRCJSON>();
            for (int i = 0; i < crcList.Count; i++)
            {
                if (File.Exists(crcList[i].FilePath))
                {
                    newCrcList.Add(crcList[i]);
                }
                else
                {
                    EDebug.Log("路径：" + crcList[i].FilePath + "下的文件已经被销毁。");
                }
            }
            crcList = newCrcList;
        }

        WriteFile(crcPath, JsonConvert.SerializeObject(crcList, Formatting.Indented));
    }

    /// <summary>
    /// 剔除没有改变的资源文件(这个没有用到)
    /// </summary>
    /// <param name="resourcesList"></param>
    private static string[] RemoveUnchangedFile(string[] resourcesList)
    {
        List<string> newList = resourcesList.ToList();
        EDebug.Log("需要遍历的文件个数：" + newList.Count);
        for (int i = newList.Count - 1; i >= 0; i--)
        {
            EditorUtility.DisplayProgressBar("正在剔除没有资源改变的文件", newList[i], (i + 1f) / newList.Count);
            if (!IsResourceFile(newList[i]) || !IsFileChanged(newList[i]))
            {
                newList.Remove(newList[i]);
            }
            EditorUtility.UnloadUnusedAssetsImmediate();
        }
        EditorUtility.ClearProgressBar();
        EDebug.Log("剔除没有资源改变文件后需要打包的文件的个数：" + newList.Count);
        return newList.ToArray();
    }

    /// <summary>
    /// 判断指定的路径是否是要打包的资源文件
    /// </summary>
    /// <param name="filePath"></param>
    private static bool IsResourceFile(string filePath)
    {
        Object obj = AssetDatabase.LoadAssetAtPath(filePath, typeof(Object));
        bool isRes = false;
        if (obj != null)
        {
            isRes = false;
            for (int k = 0; k < createList.Count; k++)
            {
                if (createList[k].Type == obj.GetType())
                {
                    isRes = true;
                    break;
                }
            }
            if (isRes)
            {
                obj = null;
                return true;
            }
        }
        return false;
    }

    /// <summary>
    /// 判断指定路径下的文件是否改变
    /// </summary>
    /// <param name="filePath"></param>
    private static bool IsFileChanged(string filePath)
    {
        //如果没有crc配置文件
        if (crcList == null || crcList.Count == 0)
        {
            return true;
        }

        string filepath = Application.dataPath + filePath.Remove(0, 6);
        ulong fileCrc = GetCrcValue(filepath);
        for (int i = 0; i < crcList.Count; i++)
        {
            if (crcList[i].FilePath.Equals(filepath))
            {
                return crcList[i].FileCRC.Equals(fileCrc);
            }
        }
        return true;
    }

    /// <summary>
    /// 获取文件的CRC值
    /// </summary>
    /// <param name="filePath"></param>
    private static ulong GetCrcValue(string filePath)
    {
        FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        int fileLen = (int)fileStream.Length;
        byte[] fileByte = new byte[fileLen];
        fileStream.Read(fileByte, 0, fileByte.Length);
        string fileStr = Encoding.UTF8.GetString(fileByte);
        ulong fileCrc = CRCData.GetCRCStr(fileStr);
        fileStream.Close();
        return fileCrc;
    }
    #endregion

    private static void WriteFile(string path, string saveStr)
    {
        string fullPath = Application.dataPath + cfgPath + path;
        StreamWriter writer = new StreamWriter(fullPath, false, Encoding.UTF8);
        writer.Write(saveStr); writer.Close();
    }
}